<?php

class QXml extends DOMDocument
{
    private $domObject;
    private $filePath;

    public function __construct()
    {
        parent::__construct();
    }

    /**
     * @return object
     */
    public function Open($file = '')
    {
        $this->filePath = $file;
        
        if (file_exists($this->filePath))
        {
            try
            {                
                $this->domObject = new $this;
                $this->domObject->load($file);                
                $this->domObject->filePath = $file;
                $this->domObject->domObject = $this->domObject;
            }
            catch (Exception $e)
            {
                echo "Bad xml";
            }

            return $this->domObject;
        }

        return false;
    }

    public function GetByTagName($name)
    {
        $elements = $this->domObject->getElementsByTagName($name);

        if (!is_null($elements) && count($elements) > 0)
        {
            $arr = array();

            foreach ($elements as $node)
            {
                $arr[] = $node->textContent;
            }

            return $arr;
        }
    // TODO add else
    }

    public function XPath($string)
    {
        if (is_object($this->domObject) && ctype_print($string))
        {
            $xp = new domXPath($this->domObject);

            $q = $xp->query($string);

            $tmpArray = array();

            foreach ($q as $node)
            {
                $tmpArray[$node->nodeName] = $node->nodeValue;
            }

            return $tmpArray;
        }
    }

    public function ToObject(DOMNode $domNode = null, $structured = true, &$baseObject = null)
    {
        $object = false;

        $domNode = (is_null($domNode)) ? $this->documentElement : $domNode;
        $baseObject = !is_null($baseObject) ? $baseObject : false;

        $ns = $domNode->lookupnamespaceURI(NULL);

        if(!is_null($ns))
        {
            $uri = parse_url($ns);

            if($_SERVER['HTTP_HOST'] != $uri['host'])
            {
                return;
            }
        }

        if (!$domNode->hasChildNodes())
        {
            return $domNode->nodeValue;
        }
        else
        {
            foreach ($domNode->childNodes AS $node)
            {
                switch ($node->nodeName)
                {
                    case '#comment':
                        break;
                    case '#text':
                        $nodVal = trim($node->nodeValue);

                        if ($nodVal == '')
                        {
                            continue;
                        }

                        switch ($nodVal)
                        {
                            case 'true':
                                $nodVal = true;
                                break;
                            case 'false':
                                $nodVal = false;
                                break;
                        }

                        if($structured)
                        {
                            $object = $nodVal;
                        }
                        else
                        {
                            if ($node->parentNode->nodeName == 'value')
                            {
                                $baseObject->{$node->parentNode->parentNode->nodeName}[] = $nodVal;
                            }
                            else
                            {
                                $baseObject->{$node->parentNode->nodeName} = $nodVal;
                            }                      
                        }
                        break;
                    default:
                    // Recursive call                    
                        $subNode = $this->ToObject($node, $structured, &$baseObject);

                        if($structured)
                        {
                            if (is_array($object->{$node->nodeName}))
                            {
                                $object->{$node->nodeName}[] = $subNode;
                            }
                            else if (!is_null($object->{$node->nodeName}))
                            {
                                $object->{$node->nodeName} = array($object->{$node->nodeName});
                                $object->{$node->nodeName}[] = $subNode;
                            }
                            else
                            {
                                $object->{$node->nodeName} = $subNode;
                            }                            
                        }
                        break;
                }
            }
        }

        return $structured ? $object : $baseObject;
    }

    public function ToArray(DOMNode $oDomNode = null, $structured = true)
    {
    // return empty array if dom is blank
        if (is_null($oDomNode) && !$this->hasChildNodes())
        {
            return array();
        }
        
        $oDomNode = (is_null($oDomNode)) ? $this->documentElement : $oDomNode;

        if (!$oDomNode->hasChildNodes())
        {
            $mResult = $oDomNode->nodeValue;
        } else
        {
            $mResult = array();
            foreach ($oDomNode->childNodes as $oChildNode)
            {
            // how many of these child nodes do we have?
            // this will give us a clue as to what the result structure should be
                $oChildNodeList = $oDomNode->getElementsByTagName($oChildNode->nodeName);
                $iChildCount = 0;
                // there are x number of childs in this node that have the same tag name
                // however, we are only interested in the # of siblings with the same tag name
                foreach ($oChildNodeList as $oNode)
                {
                    if ($oNode->parentNode->isSameNode($oChildNode->parentNode))
                    {
                        $iChildCount++;
                    }
                }
                $mValue = $this->toArray($oChildNode);
                $sKey   = ($oChildNode->nodeName{0} == '#') ? 0 : $oChildNode->nodeName;
                $mValue = is_array($mValue) ? $mValue[$oChildNode->nodeName] : $mValue;

                // how many of thse child nodes do we have?
                if ($iChildCount > 1)
                {  // more than 1 child - make numeric array
                    $mResult[$sKey][] = $mValue;
                } else
                {
                    $mResult[$sKey] = $mValue;
                }
            }
            // if the child is <foo>bar</foo>, the result will be array(bar)
            // make the result just 'bar'
            if (count($mResult) == 1 && isset($mResult[0]) && !is_array($mResult[0]))
            {
                $mResult = $mResult[0];
            }
        }
        // get our attributes if we have any
        $arAttributes = array();
        
        if ($oDomNode->hasAttributes())
        {
            foreach ($oDomNode->attributes as $sAttrName=>$oAttrNode)
            {
            // retain namespace prefixes
                $arAttributes["@{$oAttrNode->nodeName}"] = $oAttrNode->nodeValue;
            }
        }
        // check for namespace attribute - Namespaces will not show up in the attributes list
        if ($oDomNode instanceof DOMElement && $oDomNode->getAttribute('xmlns'))
        {
            $arAttributes["@xmlns"] = $oDomNode->getAttribute('xmlns');
        }
        if (count($arAttributes))
        {
            if (!is_array($mResult))
            {
                $mResult = (trim($mResult)) ? array($mResult) : array();
            }
            $mResult = array_merge($mResult, $arAttributes);
        }

        $arResult = array($oDomNode->nodeName => $mResult);
        
        return $arResult;
    }
}
